using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics.Contracts;
using System.Collections.Specialized;
using Konst3d.Efficiency.Suite;

namespace Konst3d.Efficiency.Suite.SparsedBinary
{
    internal class NoteCollectionStorage : INoteCollection, INoteStorage, IDisposable
    {
        #region Constants

        private const string NOTE_FILE_EXT = ".notes";

        #endregion // Constants

        #region Create/Open/Delete Methods

        public static NoteCollectionStorage Create(string fileName, bool overwrite, ICryptographicEngine cryptographicEngine)
        {
            Contract.Requires(!String.IsNullOrEmpty(fileName));

            NoteFile file = null;
            LabelsStorage labels = null;

            try
            {
                file = NoteFile.Create(fileName + NOTE_FILE_EXT, overwrite);
                labels = LabelsStorage.Create(fileName, overwrite, cryptographicEngine);

                return new NoteCollectionStorage(file, labels);
            }
            catch
            {
                if (file != null)
                {
                    file.Dispose();
                }

                if (labels != null)
                {
                    labels.Dispose();
                }

                throw;
            }
        }

        public static NoteCollectionStorage Open(string fileName, ICryptographicEngine cryptographicEngine)
        {
            Contract.Requires(!String.IsNullOrEmpty(fileName));

            NoteFile file = null;
            LabelsStorage labels = null;

            try
            {
                file = NoteFile.Open(fileName + NOTE_FILE_EXT);
                labels = LabelsStorage.Open(fileName, cryptographicEngine);

                return new NoteCollectionStorage(file, labels);
            }
            catch
            {
                if (file != null)
                {
                    file.Dispose();
                }

                if (labels != null)
                {
                    labels.Dispose();
                }

                throw;
            }
        }

        public void Delete()
        {
            Contract.Assert(!_disposed);

            _file.Delete();
            _labels.Delete();

            Dispose();
        }

        #endregion // Create/Open/Delete Methods

        #region Constructor/Finalizer

        private NoteCollectionStorage(NoteFile file, LabelsStorage labels)
        {
            Contract.Requires(file != null);
            Contract.Requires(labels != null);

            _file = file;
            _labels = labels;

            var entries = _file.GetAll();

            foreach (var entry in entries)
            {
                _notes.Add(entry.Key, new Note(this, entry.Key, _labels.GetLabel(entry.Value.TitleID), _labels.GetLabel(entry.Value.TextID), entry.Value.Created));
            }
        }

        ~NoteCollectionStorage()
        {
            Dispose(false);
        }

        #endregion // Constructor/Finalizer

        #region INoteCollection Members

        public int Count
        {
            get
            {
                return _notes.Count;
            }
        }

        public INote this[int id]
        {
            get
            {
                Contract.Assert(_notes.ContainsKey(id));

                return _notes[id];
            }
        }

        public int Add(string title, string text)
        {
            int titleID = _labels.AddLabel(title);
            int textID = _labels.AddLabel(text);

            int id = _file.AddEntry(titleID, textID, DateTime.Now);

            var note = new Note(this, id, title, text, DateTime.Now);
            _notes.Add(id, note);

            if (CollectionChanged != null)
            {
                var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset); // TOFIX: optimize
                CollectionChanged(this, args);
            }

            return id;
        }

        public void Remove(int id)
        {
            Contract.Assert(_notes.ContainsKey(id));

            var entry = _file[id];

            _labels.DeleteLabel(entry.TitleID);
            _labels.DeleteLabel(entry.TextID);

            _file.RemoveEntry(id);
            _notes.Remove(id);

            if (CollectionChanged != null)
            {
                var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset); // TOFIX: optimize
                CollectionChanged(this, args);
            }
        }

        #endregion

        #region INotifyCollectionChanged Members

        public event NotifyCollectionChangedEventHandler CollectionChanged;

        #endregion

        #region IEnumerable<INote> Members

        public IEnumerator<INote> GetEnumerator()
        {
            return _notes.Values.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return _notes.Values.GetEnumerator();
        }

        #endregion

        #region INoteStorage Members

        void INoteStorage.UpdateTitle(int id, string title)
        {
            Contract.Assert(_notes.ContainsKey(id));

            int titleID = _file.GetEntryTitleID(id);
            int newTitleID = _labels.UpdateLabel(titleID, title);

            _file.SetEntryTitleID(id, newTitleID);

            if (CollectionChanged != null)
            {
                CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); // TOFIX: optimize
            }
        }

        void INoteStorage.UpdateText(int id, string text)
        {
            Contract.Assert(_notes.ContainsKey(id));

            int textID = _file.GetEntryTextID(id);
            int newTextID = _labels.UpdateLabel(textID, text);

            _file.SetEntryTextID(id, newTextID);

            if (CollectionChanged != null)
            {
                CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); // TOFIX: optimize
            }
        }

        void INoteStorage.UpdateCreated(int id, DateTime createdDate)
        {
            _file.SetEntryCreatedDate(id, createdDate);

            if (CollectionChanged != null)
            {
                CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); // TOFIX: optimize
            }
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            GC.SuppressFinalize(this);
            Dispose(true);
        }

        private void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    _file.Dispose();
                    _file = null;

                    _labels.Dispose();
                    _labels = null;

                    _notes.Clear();
                    _notes = null;
                }

                _disposed = true;
            }
        }

        #endregion

        #region Fields

        private bool _disposed = false;
        private Dictionary<int, Note> _notes = new Dictionary<int, Note>();
        private NoteFile _file;
        private LabelsStorage _labels;

        #endregion // Fields
    }
}